/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.aruoc.sim;

import org.aruoc.sim.siguelineas.*;
import org.aruoc.sim.SimWorld;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.event.KeyAdapter;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferStrategy;
import java.util.Vector;
import javax.swing.JFrame;
import net.phys2d.math.MathUtil;
import net.phys2d.math.Matrix2f;
import net.phys2d.math.ROVector2f;
import net.phys2d.math.Vector2f;
import net.phys2d.raw.BasicJoint;
import net.phys2d.raw.Body;
import net.phys2d.raw.Contact;
import net.phys2d.raw.FixedJoint;
import net.phys2d.raw.Joint;
import net.phys2d.raw.SpringJoint;
import net.phys2d.raw.shapes.Box;
import net.phys2d.raw.shapes.Circle;
import net.phys2d.raw.shapes.Line;
import net.phys2d.raw.shapes.Polygon;

/**
 *
 * @mperezma
 */
public class SimWorldPainter {

    public boolean pintarTerreno = true;
    private Canvas canvas;
    private BufferStrategy strategy;
    private SimWorld world;
    JFrame frame;
    private float informedFrameAverage;
//    private long informedRenderTime;
//    private long informedLogicTime;
    private long horaUltimoFrame = System.currentTimeMillis();
    private Vector<Float> frameAverages = new Vector<Float>();
    public boolean dibujarEstela;
//    private long averagedRenderTime;
//    private long averagedLogicTime;
    public SimWorldPainter(SimWorld world) {

        //frame = new WorldFrame();
        frame = new JFrame();

        //frame.setSize(1024, 768);
        canvas = new Canvas();
        canvas.setSize(1024, 768);
        frame.add(canvas);
        frame.pack();
        frame.setVisible(true);

        canvas.createBufferStrategy(2);
        canvas.setIgnoreRepaint(true);
        strategy = canvas.getBufferStrategy();

        addKeyListener(world.getKeyAdapter());
        this.world = world;

        addWindowListener(new WindowAdapter() {

            @Override
            public void windowClosing(WindowEvent e) {
                Main.executing = false;
            }
        });


    }

    public void addWindowListener(WindowAdapter wa) {
        frame.addWindowListener(wa);
    }

    public void addKeyListener(KeyAdapter ka) {
        frame.addKeyListener(ka);
    }

    public void drawFilledCircleBody(Graphics2D g, Body body, Circle circle, Color color) {
        g.setColor(color);
        float x = body.getPosition().getX();
        float y = body.getPosition().getY();
        float r = circle.getRadius();
        float rot = body.getRotation();
        float xo = (float) (Math.cos(rot) * r);
        float yo = (float) (Math.sin(rot) * r);

        g.fillOval((int) (x - r), yReal((int) (y - r)), (int) (r * 2), (int) (r * 2));
        g.drawLine((int) x, yReal((int) y), (int) (x + xo), yReal((int) (y + yo)));
    }

    private void calcularFrameAverage() {
        frameAverages.add(Main.frameAverage);
//        averagedRenderTime = (Main.renderTime + averagedRenderTime) / 2;
//        averagedLogicTime = (Main.logicTime + averagedLogicTime) / 2;

        if (System.currentTimeMillis() - horaUltimoFrame > 1000) {
            for (Float average : frameAverages) {
                informedFrameAverage += average;
            }

            informedFrameAverage /= frameAverages.size();
//            informedRenderTime = averagedRenderTime;
//            informedLogicTime = averagedLogicTime;
            horaUltimoFrame = System.currentTimeMillis();
            frameAverages.clear();
        }
    }

    // TODO 
    private int yReal(int y) {
        //return canvas.getHeight() - y;
        return y;
    }
    /**
     * Draw the whole simulation
     * 
     * @param g The graphics context on which to draw
     */
    Graphics2D g;

    public void draw() {
        g = (Graphics2D) strategy.getDrawGraphics();


        // En principio no haría falta blanquear porque se dibuja la pista
        // Pero por si acaso la pista es más pequeña que el canvas.
        if (!dibujarEstela) {
            g.setColor(Color.white);
            g.fillRect(0, 0, canvas.getWidth(), canvas.getHeight());

            // TODO pintarTerreno debe estar en world. Si el terreno está desactivado o no puede leerlo = false
            if (pintarTerreno) {
                g.drawImage(world.getImagen(), 0, 0, canvas);
            }

        } else {
            g.setColor(Color.white);
            g.fillRect(0, 700, 220, 790);
            
        }

        g.setColor(Color.BLACK);
        g.drawString(Main.LINE1, 10, 20);
        g.drawString(Main.LINE2, 10, 40);
        g.setColor(Color.BLUE);
        g.drawString(Main.LINE3, 10, 60);

        calcularFrameAverage();

        g.setColor(Color.BLACK);
//        g.drawString("Tiempo de pintado: " + informedRenderTime + "ms", 10, 720);
//        g.drawString("Tiempo de CPU: " + informedLogicTime + "ms", 10, 740);
        g.drawString("Escena número: " + world.currentTime(), 10, 720);
        g.drawString("Tiempo real de simulación: " + (System.currentTimeMillis() - Main.inicioSimulacion)+"ms", 10, 740);
        g.drawString("FPS: " + (1000 / informedFrameAverage), 10, 760);

        for (Sim sim : world.getSims()) {
            sim.paint(this, g);
        }


//        BodyList bodies = world.getBodies();
//
//        for (int i = 0; i < bodies.size(); i++) {
//            Body body = bodies.get(i);
//
//            drawBody(g, body);
//        }
//
//        JointList joints = world.getJoints();
//
//        for (int i = 0; i < joints.size(); i++) {
//            Joint joint = joints.get(i);
//
//            drawJoint(g, joint);
//        }
//
//        ArbiterList arbs = world.getArbiters();
//
//        for (int i = 0; i < arbs.size(); i++) {
//            Arbiter arb = arbs.get(i);
//
//            Contact[] contacts = arb.getContacts();
//            int numContacts = arb.getNumContacts();
//
//            for (int j = 0; j < numContacts; j++) {
//                drawContact(g, contacts[j]);
//            }
//        }

        strategy.show();
    }

    /**
     * Draw a specific contact point determined from the simulation
     * 
     * @param g The graphics context on which to draw
     * @param contact The contact to draw
     */
    protected void drawContact(Graphics2D g, Contact contact) {
        int x = (int) contact.getPosition().getX();
        int y = (int) contact.getPosition().getY();
        //if (contacts) {
        g.setColor(Color.blue);
        g.fillOval(x - 3, y - 3, 6, 6);
        //}

        //if (normals) {
        int dx = (int) (contact.getNormal().getX() * 10);
        int dy = (int) (contact.getNormal().getY() * 10);
        g.setColor(Color.darkGray);
        g.drawLine(x, y, x + dx, y + dy);
    //}
    }

    /**
     * Draw a body 
     * 
     * @param g The graphics contact on which to draw
     * @param body The body to be drawn
     */
    protected void drawBody(Graphics2D g, Body body) {
        if (body.getShape() instanceof Box) {
            drawBoxBody(g, body, (Box) body.getShape());
        }
        if (body.getShape() instanceof Circle) {
            drawCircleBody(g, body, (Circle) body.getShape());
        }
        if (body.getShape() instanceof Line) {
            drawLineBody(g, body, (Line) body.getShape());
        }
        if (body.getShape() instanceof Polygon) {
            drawPolygonBody(g, body, (Polygon) body.getShape());
        }
    }

    /**
     * Draw a polygon into the demo
     * 
     * @param g The graphics to draw the poly onto
     * @param body The body describing the poly's position
     * @param poly The poly to be drawn
     */
    protected void drawPolygonBody(Graphics2D g, Body body, Polygon poly) {
        g.setColor(Color.black);

        ROVector2f[] verts = poly.getVertices(body.getPosition(), body.getRotation());
        for (int i = 0,  j = verts.length - 1; i < verts.length; j = i, i++) {
            g.drawLine((int) (0.5f + verts[i].getX()),
                    (int) (0.5f + verts[i].getY()),
                    (int) (0.5f + verts[j].getX()),
                    (int) (0.5f + verts[j].getY()));
        }
    }

    /**
     * Draw a line into the demo
     * 
     * @param g The graphics to draw the line onto
     * @param body The body describing the line's position
     * @param line The line to be drawn
     */
    protected void drawLineBody(Graphics2D g, Body body, Line line) {
        g.setColor(Color.black);
        //
//		float x = body.getPosition().getX();
//		float y = body.getPosition().getY();
//		float dx = line.getDX();
//		float dy = line.getDY();
//		
//		g.drawLine((int) x,(int) y,(int) (x+dx),(int) (y+dy));
        Vector2f[] verts = line.getVertices(body.getPosition(), body.getRotation());
        g.drawLine((int) verts[0].getX(),
                (int) verts[0].getY(),
                (int) verts[1].getX(),
                (int) verts[1].getY());
    }

    void drawLineBody(Body body, Line line) {
        g.setColor(Color.black);
        //
//		float x = body.getPosition().getX();
//		float y = body.getPosition().getY();
//		float dx = line.getDX();
//		float dy = line.getDY();
//		
//		g.drawLine((int) x,(int) y,(int) (x+dx),(int) (y+dy));
        Vector2f[] verts = line.getVertices(body.getPosition(), body.getRotation());
        g.drawLine((int) verts[0].getX(),
                (int) verts[0].getY(),
                (int) verts[1].getX(),
                (int) verts[1].getY());

    }

    void drawLine(Line line) {
        g.setColor(Color.black);
        //
//		float x = body.getPosition().getX();
//		float y = body.getPosition().getY();
//		float dx = line.getDX();
//		float dy = line.getDY();
//		
//		g.drawLine((int) x,(int) y,(int) (x+dx),(int) (y+dy));
        g.drawLine((int) line.getX1(),
                (int) line.getY1(),
                (int) line.getX2(),
                (int) line.getY2());
    }

    /**
     * Draw a circle in the world
     * 
     * @param g The graphics contact on which to draw
     * @param body The body to be drawn
     * @param circle The shape to be drawn
     */
    protected void drawCircleBody(Graphics2D g, Body body, Circle circle) {
        g.setColor(Color.black);
        float x = body.getPosition().getX();
        float y = body.getPosition().getY();
        float r = circle.getRadius();
        float rot = body.getRotation();
        float xo = (float) (Math.cos(rot) * r);
        float yo = (float) (Math.sin(rot) * r);

        g.drawOval((int) (x - r), yReal((int) (y - r)), (int) (r * 2), (int) (r * 2));
        g.drawLine((int) x, yReal((int) y), (int) (x + xo), yReal((int) (y + yo)));
    }

    /**
     * Draw a box in the world
     * 
     * @param g The graphics contact on which to draw
     * @param body The body to be drawn
     * @param box The shape to be drawn
     */
    protected void drawBoxBody(Graphics2D g, Body body, Box box) {
        Vector2f[] pts = box.getPoints(body.getPosition(), body.getRotation());

        Vector2f v1 = pts[0];
        Vector2f v2 = pts[1];
        Vector2f v3 = pts[2];
        Vector2f v4 = pts[3];

        g.setColor(Color.black);
        g.drawLine((int) v1.x, yReal((int) v1.y), (int) v2.x, yReal((int) v2.y));
        g.drawLine((int) v2.x, yReal((int) v2.y), (int) v3.x, yReal((int) v3.y));
        g.drawLine((int) v3.x, yReal((int) v3.y), (int) v4.x, yReal((int) v4.y));
        g.drawLine((int) v4.x, yReal((int) v4.y), (int) v1.x, yReal((int) v1.y));
    }

    /**
     * Draw a joint 
     * 
     * @param g The graphics contact on which to draw
     * @param j The joint to be drawn
     */
    public void drawJoint(Graphics2D g, Joint j) {
        if (j instanceof FixedJoint) {
            FixedJoint joint = (FixedJoint) j;

            g.setColor(Color.red);
            float x1 = joint.getBody1().getPosition().getX();
            float x2 = joint.getBody2().getPosition().getX();
            float y1 = joint.getBody1().getPosition().getY();
            float y2 = joint.getBody2().getPosition().getY();

            g.drawLine((int) x1, (int) y1, (int) x2, (int) y2);
        }
        if (j instanceof BasicJoint) {
            BasicJoint joint = (BasicJoint) j;

            Body b1 = joint.getBody1();
            Body b2 = joint.getBody2();

            Matrix2f R1 = new Matrix2f(b1.getRotation());
            Matrix2f R2 = new Matrix2f(b2.getRotation());

            ROVector2f x1 = b1.getPosition();
            Vector2f p1 = MathUtil.mul(R1, joint.getLocalAnchor1());
            p1.add(x1);

            ROVector2f x2 = b2.getPosition();
            Vector2f p2 = MathUtil.mul(R2, joint.getLocalAnchor2());
            p2.add(x2);

            g.setColor(Color.red);
            g.drawLine((int) x1.getX(), (int) x1.getY(), (int) p1.x, (int) p1.y);
            g.drawLine((int) p1.x, (int) p1.y, (int) x2.getX(), (int) x2.getY());
            g.drawLine((int) x2.getX(), (int) x2.getY(), (int) p2.x, (int) p2.y);
            g.drawLine((int) p2.x, (int) p2.y, (int) x1.getX(), (int) x1.getY());
        }
        if (j instanceof SpringJoint) {
            SpringJoint joint = (SpringJoint) j;

            Body b1 = joint.getBody1();
            Body b2 = joint.getBody2();

            Matrix2f R1 = new Matrix2f(b1.getRotation());
            Matrix2f R2 = new Matrix2f(b2.getRotation());

            ROVector2f x1 = b1.getPosition();
            Vector2f p1 = MathUtil.mul(R1, joint.getLocalAnchor1());
            p1.add(x1);

            ROVector2f x2 = b2.getPosition();
            Vector2f p2 = MathUtil.mul(R2, joint.getLocalAnchor2());
            p2.add(x2);

            g.setColor(Color.red);
            g.drawLine((int) x1.getX(), (int) x1.getY(), (int) p1.x, (int) p1.y);
            g.drawLine((int) p1.x, (int) p1.y, (int) p2.getX(), (int) p2.getY());
            g.drawLine((int) p2.getX(), (int) p2.getY(), (int) x2.getX(), (int) x2.getY());
        }
    }
}
